#pragma once

#include <iostream>
#include <alloca.h>
#include <exception>
#include <vector>
#include <unordered_map>
#include <cstdint>
#include <string>
#include <exception>
#include "mockturtle/mockturtle.hpp"

template <class TT>
bool bottom_mux_decomposition(const TT &tt, uint32_t sel, uint32_t var_index1, uint32_t var_index2, TT *func = nullptr)
{
    const auto co0_var1 = cofactor0(tt, var_index1);
    const auto co1_var1 = cofactor1(tt, var_index1);
    const auto dif_var1 = kitty::binary_xor(co0_var1, co1_var1);

    const auto co0_var2 = cofactor0(tt, var_index2);
    const auto co1_var2 = cofactor1(tt, var_index2);
    const auto dif_var2 = kitty::binary_xor(co0_var2, co1_var2);

    const auto dif1_co0_var2 = cofactor0(dif_var1, var_index2);
    const auto dif1_co1_var2 = cofactor1(dif_var1, var_index2);
    const auto dif1_dif2 = kitty::binary_xor(dif1_co0_var2, dif1_co1_var2);

    const auto co1_dif1_sel = cofactor1(dif_var1, sel);
    const auto co0_dif2_sel = cofactor0(dif_var2, sel);

    const auto co0_var1_var2 = cofactor0(cofactor0(tt, var_index1), var_index2);
    const auto co1_var1_var2 = cofactor1(cofactor1(tt, var_index1), var_index2);

    const auto dif0_sel = kitty::binary_xor(cofactor0(co0_var1_var2, sel), cofactor1(co0_var1_var2, sel));
    const auto dif1_sel = kitty::binary_xor(cofactor0(co1_var1_var2, sel), cofactor1(co1_var1_var2, sel));

    const auto co0_dif1_sel = cofactor0(dif_var1, sel);

    if (equal(dif_var1, dif_var2))
    {
        return false;
    }

    if (is_const0(dif1_dif2) & is_const0(co1_dif1_sel) & is_const0(co0_dif2_sel) & is_const0(dif0_sel) & is_const0(dif1_sel))
    {
        if (func)
        {
            const auto co0_dif1_sel = cofactor0(dif_var1, sel);
            *func = kitty::binary_and(mux_var(sel, ~co0_var1_var2, co0_var1_var2), co0_dif1_sel);
        }
        return true;
    }
    return false;
}